package socketmvc.server.context;

import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.core.TioConfig;
import org.tio.utils.lock.ListWithLock;
import org.tio.utils.lock.ReadLockHandler;
import org.tio.utils.lock.SetWithLock;
import org.tio.utils.lock.WriteLockHandler;
import socketmvc.core.config.SocketMvcConfig;
import socketmvc.core.lambda.DoubleValConsumer;
import socketmvc.core.packets.basic.IPacket;
import socketmvc.server.SocketServerStarter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * im服务上下文
 * @author xueyanjun
 */
public class SocketServerContext implements IServerContext {

    private final SocketServerStarter server;

    private TioConfig ioConfig;

    private final SocketMvcConfig imConfig;

    public SocketServerContext(SocketServerStarter server, TioConfig config, SocketMvcConfig imConfig) {
        this.server = server;
        this.ioConfig = config;
        this.imConfig = imConfig;
    }

    public void setIoConfig(TioConfig ioConfig){
        this.ioConfig = ioConfig;
    }

    @Override
    public Object getAttr(String key) {
        return ioConfig.getAttribute(key);
    }


    @Override
    public Object setAttr(String key, Object val) {
        ioConfig.setAttribute(key,val);
        return val;
    }

    @Override
    public SocketServerStarter getServer() {
        return server;
    }

    @Override
    public TioConfig getIoConfig() {
        return ioConfig;
    }

    @Override
    public SocketMvcConfig getMvcConfig() {
        return imConfig;
    }

    private String getType(ChannelContext context) {
        String name = context.getTioConfig().getName();
        name = name.substring(0,name.lastIndexOf(":"));
        return switch (name){
           case "socketmvc-ws-server" -> "ws";
           case "socketmvc-wss-server" -> "wss";
           case "socketmvc-tcp-server" -> "tcp";
           default -> server.getName();
        };
    }

    protected IChannelContext getImContext(ChannelContext context){
        if (context == null){
            return null;
        }
        SocketChannel imContext = new SocketChannel(context, getType(context));
        imContext.setServerContext(this);
        return imContext;
    }

    protected List<IChannelContext> getImContext(SetWithLock<ChannelContext> contexts){
        if (contexts == null || contexts.size() == 0){
            return null;
        }
        List<IChannelContext> imContext = new ArrayList<>(contexts.size());
        contexts.handle((ReadLockHandler<Set<ChannelContext>>) channelContexts -> {
            for (ChannelContext channelContext : channelContexts) {
                imContext.add(getImContext(channelContext));
            }
        });
        return imContext;
    }


    @Override
    public List<IChannelContext> getByUser(String user){
        SetWithLock<ChannelContext> contexts = Tio.getByUserid(ioConfig, user);
        return getImContext(contexts);
    }

    @Override
    public ListWithLock<IChannelContext> getByUserWithLock(String user){
        List<IChannelContext> userContexts = getByUser(user);
        if (userContexts == null){
            return null;
        }
       return new ListWithLock<>(userContexts);
    }

    @Override
    public List<IChannelContext> getByGroup(String group){
        SetWithLock<ChannelContext> contexts = Tio.getByGroup(ioConfig, group);
        return getImContext(contexts);
    }

    @Override
    public ListWithLock<IChannelContext> getByGroupWithLock(String group){
        List<IChannelContext> userContexts = getByGroup(group);
        if (userContexts == null){
            return null;
        }
        return new ListWithLock<>(userContexts);
    }

    @Override
    public IChannelContext getByCurrentThread(){
        return ThreadSocketChannelHandler.get();
    }

    @Override
    public void send(ListWithLock<IChannelContext> contexts, IPacket packet) {
        contexts.handle((ReadLockHandler<List<IChannelContext>>) SocketChannelContexts -> {
            for (IChannelContext context : SocketChannelContexts) {
                send(context,packet);
            }
        });
    }

    @Override
    public void send(List<IChannelContext> contexts, IPacket packet) {
        for (IChannelContext context : contexts) {
            send(context,packet);
        }
    }

    @Override
    public void send(IChannelContext context, IPacket packet){
        context.send(packet);
    }

    @Override
    public void sendToUser(String user, IPacket packet) {
        sendToUserNullable(user,packet,(u,p)->{
            throw new RuntimeException(String.format("未找到user[ %s ] , 不能发送数据包",u));
        });
    }

    @Override
    public void sendToUserNullable(String user, IPacket packet, DoubleValConsumer<String,IPacket> nullableCall){
        ListWithLock<IChannelContext> userWithLock = getByUserWithLock(user);
        if (userWithLock == null || userWithLock.size() == 0){
            if (nullableCall != null){
                nullableCall.accept(user,packet);
            }
            return;
        }
        send(userWithLock,packet);
    }

    @Override
    public void sendToGroup(String group, IPacket packet) {
        sendToGroupNullable(group,packet,(g,p)->{
            throw new RuntimeException(String.format("未找到group[ %s ] , 不能发送数据包",g));
        });
    }

    @Override
    public void sendToGroupNullable(String group, IPacket packet, DoubleValConsumer<String,IPacket> nullableCall){
        ListWithLock<IChannelContext> groupWithLock = getByGroupWithLock(group);
        if (groupWithLock == null || groupWithLock.size() == 0){
            if (nullableCall != null){
                nullableCall.accept(group,packet);
            }
            return;
        }
        send(groupWithLock,packet);
    }

    @Override
    public void sendToCurrentThread(IPacket packet){
        getByCurrentThread().send(packet);
    }

    @Override
    public void bindUser(IChannelContext context, String user) {
        context.bindUser(user);
    }

    @Override
    public void bindGroup(IChannelContext context, String group) {
        context.bindGroup(group);
    }

    @Override
    public void unbindUser(IChannelContext context, String user) {
        context.unbindUser(user);
    }

    @Override
    public void unbindUser(String user) {
        ListWithLock<IChannelContext> userWithLock = getByUserWithLock(user);
        if (userWithLock == null || userWithLock.size() ==0){
            return;
        }
        userWithLock.handle((WriteLockHandler<List<IChannelContext>>) IChannelContexts -> {
            for (IChannelContext IChannelContext : IChannelContexts) {
                unbindUser(IChannelContext,user);
            }
        });
    }

    @Override
    public void unbindGroup(IChannelContext context, String group) {
        context.unbindGroup(group);
    }

    @Override
    public void unbindGroup(String user, String group) {
        ListWithLock<IChannelContext> userWithLock = getByUserWithLock(user);
        if (userWithLock == null || userWithLock.size() ==0){
            return;
        }
        userWithLock.handle((ReadLockHandler<List<IChannelContext>>) IChannelContexts -> {
            for (IChannelContext IChannelContext : IChannelContexts) {
                unbindGroup(IChannelContext,group);
            }
        });
    }


    @Override
    public boolean getUserIsOnline(String user) {
        List<IChannelContext> users = getByUser(user);
        return user!=null && !users.isEmpty();
    }


    @Override
    public void close(IChannelContext context) {
        context.close();
    }

    @Override
    public void close(List<IChannelContext> context) {
        for (IChannelContext IChannelContext : context) {
            IChannelContext.close();
        }
    }

    @Override
    public void close(ListWithLock<IChannelContext> context) {
        context.handle((WriteLockHandler<List<IChannelContext>>) IChannelContexts -> {
            for (IChannelContext IChannelContext : IChannelContexts) {
                IChannelContext.close();
            }
        });
    }

    /**
     * 将客户端设置进黑名单
     *
     * @param context 网络连接上下文
     */
    @Override
    public void setContextToBlacklist(IChannelContext context) {
        context.setContextToBlacklist();
    }

    /**
     * 从黑名单中删除客户端
     *
     * @param context 网络连接上下文
     */
    @Override
    public void delContextFromBlacklist(IChannelContext context) {
        context.delContextFromBlacklist();
    }

    /**
     * 查看客户端是否在黑名单中
     *
     * @param context 网络连接上下文
     * @return true在黑名单中，false不在
     */
    @Override
    public boolean isContextInBlacklist(IChannelContext context) {
        return context.isContextInBlacklist();
    }

}
